#ifndef __TEXTURE_H
#define __TEXTURE_H

#include "matrix.h"

/*
class FRGBA 
{
public:
        FRGBA () : r(color[0]), g(color[1]), b(color[2]), a(color[3]) 
        {
                r = g = b = a = 0.0; 
        }
        FRGBA (float _r, float _g, float _b, float _a)
                : r(color[0]), g(color[1]), b(color[2]), a(color[3])
        {
                r = _r, g = _g, b = _b, a = _a; 
        }
public :
        //float r, g, b , a; 
        math::vector<float, 4> color ; 
        float & r, & g, & b,  & a; 
};
*/

class TexCoord
{
public:
        TexCoord () : x(0.0f), y (0.0f) {}
        TexCoord (float _x, float _y) : x(_x), y (_y) {}
public:
        float x, y ;
};

class TextureMemory2D
{
public :
        /**
         * height --> row --> y
         * width  --> col --> x
         */
		
        typedef math::vector<float, 4> frgba_type ;
        TextureMemory2D (unsigned row, unsigned col) : m_row (row), m_col (col) 
        {
                this->m_videoMemory = new float [4 * m_row * m_col] ;
                if (this->m_videoMemory == NULL ) throw ("Memory Allocation Fail.");
        }
        unsigned get_col() const {return this->m_col ; }
        unsigned get_row() const {return this->m_row ; }

        float get_texXOffset () const {return 1.0; }
        float get_texYOffset () const {return 1.0 ; }
        float get_texOffset () const { return 1.0;}

        float * get_memory () const 
        {
                return this->m_videoMemory ;
        }


        TexCoord get_texCoord (unsigned row, unsigned col ) 
        {
                TexCoord coord ;
                coord.x = col; 
                coord.y = row; 
                return coord ;
        }

        void read (unsigned row, unsigned col, frgba_type & color) 
        {
                unsigned offset = get_offset(row, col ); 
                color.r = m_videoMemory[offset + 0]; 
                color.g = m_videoMemory[offset + 1]; 
                color.b = m_videoMemory[offset + 2]; 
                color.a = m_videoMemory[offset + 3]; 
        }
        void write (unsigned row, unsigned col, const frgba_type & color) 
        {
                unsigned offset = get_offset (row, col);
                m_videoMemory[offset + 0] = color.r ;
                m_videoMemory[offset + 1] = color.g ;
                m_videoMemory[offset + 2] = color.b ;
                m_videoMemory[offset + 3] = color.a ;           
        }

        void write (unsigned row, unsigned col, unsigned offset, const float value)
        {
                offset += get_offset(row, col);
                m_videoMemory[offset] = value ; 
        }

        ~TextureMemory2D () {delete this->m_videoMemory ;}
protected :
        /* Col has high priority. */
        unsigned get_offset (unsigned row, unsigned col) { return 4 * ( row * m_col + col) ; }
private :
        unsigned m_col, m_row ;
        float * m_videoMemory ;

};

class Texture 
{
public :
        typedef math::vector<float, 4> frgba_type ;

        Texture (unsigned height, unsigned width): m_row(height), m_col(width) 
        {               
                mp_texture = new TextureMemory2D (height, width ) ;
                mp_subtexture = new TextureMemory2D (1, 1);
        }
        ~ Texture () 
        {
                delete mp_texture ; 
                delete mp_subtexture ;
        }
public :
        void init()
        {
                std::cout<<"INIT TEX BEGIN"<<std::endl;
                glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
                std::cout<<"INIT TEX BEGIN 1"<<std::endl;
                glGenTextures(1, &m_texName);
                std::cout<<"INIT TEX BEGIN 2"<<std::endl;
                glBindTexture(GL_TEXTURE_RECTANGLE, m_texName);
                std::cout<<"INIT TEX BEGIN 3"<<std::endl;
                glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_REPEAT);
                std::cout<<"INIT TEX BEGIN 4"<<std::endl;
                glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_REPEAT);
                std::cout<<"INIT TEX BEGIN 5"<<std::endl;
                glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, 
                                                GL_NEAREST);
                std::cout<<"INIT TEX BEGIN 6"<<std::endl;
                glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, 
                                                GL_NEAREST);
                std::cout<<"INIT TEX BEGIN 7"<<std::endl;
                glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
                std::cout<<"INIT TEX BEGIN 8"<<std::endl;
/*
                glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA32F_ARB, mp_texture->get_col(), mp_texture->get_row(), 0, GL_RGBA, GL_FLOAT, mp_texture->get_memory() );
*/
                glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA32F_ARB, mp_texture->get_col(), mp_texture->get_row(), 0, GL_RGBA, GL_FLOAT, mp_texture->get_memory() );
                std::cout<<"INIT TEX END"<<std::endl;
        }


        void write (unsigned row, unsigned col, const frgba_type & color)
        {
                this->mp_texture->write (row, col, color);
        }
        void update (unsigned row, unsigned col, const frgba_type & color)
        {
                this->mp_subtexture->write (0, 0, color);
                //glBindTexture(GL_TEXTURE_RECTANGLE, m_texName);
                glTexSubImage2D (GL_TEXTURE_RECTANGLE, 0, 
                                                 col, row, 
                                                 1, 1, GL_RGBA, GL_FLOAT, mp_subtexture->get_memory()); 
        }
private :
        GLuint m_texName;
        unsigned m_row, m_col;
        TextureMemory2D * mp_texture ;
        TextureMemory2D * mp_subtexture; 
};
#endif

